好,首先我們預期會讓很多使用者登入後上傳資料,所以我們需要搞個登入登出實作,預計會用 session 來弄
首先今天程式碼量比較多
以下路徑
src/routes/api/login/+server.ts
import type { RequestHandler } from './$types';
import { hashPassword } from '$lib/server/auth';
import type { ClientUsers } from '$lib/server/schema/ClientUsers';
export const POST: RequestHandler = async ({ request, cookies, platform }) => {
	const { username, password } = await request.json();
	const stmt = await platform.env.DB.prepare('SELECT * FROM client_users WHERE username = ?');
	const { results } = await stmt.bind(username).run();
	console.log(results);
	if (!Array.isArray(results) || results.length === 0) {
		return new Response(JSON.stringify({ message: 'User not found' }), { status: 401 });
	}
	const user = results[0] as ClientUsers;
	const passwordHash = await hashPassword(password);
	if (passwordHash !== user.password) {
		return new Response(JSON.stringify({ error: 'Invalid' }), { status: 401 });
	}
	const sessionId = crypto.randomUUID();
	cookies.set('session_id', sessionId, {
		httpOnly: true,
		path: '/',
		secure: true,
		sameSite: 'lax',
		maxAge: 60 * 30
	});
	const insert_stmt = await platform.env.DB.prepare(
		'INSERT INTO client_users_session(sid, user_id,expires_at) VALUES (?, ?,DATETIME("now", "+30 minutes"))'
	);
	await insert_stmt.bind(sessionId, user.id).run();
	return new Response(JSON.stringify({ message: 'Login success' }));
};
src/routes/api/register/+server.ts
import type { RequestHandler } from './$types';
import { hashPassword } from '$lib/server/auth';
export const POST: RequestHandler = async ({ request, platform }) => {
	const { username, password } = await request.json();
	if (!username || !password) {
		return new Response(JSON.stringify({ message: 'Username and password required' }), {
			status: 400
		});
	}
	// 檢查是否已存在
	const query_stmt = await platform.env.DB.prepare('SELECT * FROM client_users WHERE username = ?');
	const { results } = await query_stmt.bind(username).run();
	if (Array.isArray(results) && results.length > 0) {
		return new Response(JSON.stringify({ message: 'Username already exists' }), { status: 409 });
	}
	// hash 密碼
	const passwordHash = await hashPassword(password);
	// 寫入資料庫
	const insert_stmt = await platform.env.DB.prepare(
		'INSERT INTO client_users(username, password) VALUES (?, ?)'
	);
	await insert_stmt.bind(username, passwordHash).run();
	return new Response(JSON.stringify({ message: 'Register success' }), { status: 201 });
};
src/routes/api/logout/+server.ts
import type { RequestHandler } from './$types';
export const DELETE: RequestHandler = async ({ cookies, platform }) => {
	// 讀出 session_id
	const sessionId = cookies.get('session_id');
	if (sessionId) {
		const insert_stmt = await platform.env.DB.prepare(
			'DELETE FROM  client_users_session where sid =?'
		);
		await insert_stmt.bind(sessionId).run();
	}
	// 清除 cookie
	cookies.set('session_id', '', {
		path: '/',
		expires: new Date(0), // 立即過期
		httpOnly: true,
		secure: true,
		sameSite: 'lax'
	});
	return new Response(JSON.stringify({ success: true }));
};
我講幾個雷點
1、lib 裡面的資料夾如果要給 server 使用,要先開個 server 資料夾,不能直接 call,他會前後不分
2、本來查到 node 可以用 bcrypt 或 crypto ,想說 crypto  原生內建,直接用好了,結果爆炸,因為 wokers 要這樣用,要特別設定,秉持著能原生就原生的精神,我們採用 Web Crypto API,連 import 都不需要可以直接使用,超讚
今天的情緒抒發:
突然發現是不是有點寫不完阿